home *** CD-ROM | disk | FTP | other *** search
/ The Arsenal Files 8 / The Arsenal Files Collection #8 (Arsenal Computer) (1996).ISO / prg_gen / euphor14.zip / ED.EX < prev    next >
Text File  |  1996-10-15  |  49KB  |  1,959 lines

  1.     ----------------------------------------------------------
  2.     --       This Euphoria Editor was developed by          --
  3.     --            Rapid Deployment Software.                --
  4.     --                                                      --
  5.     -- Permission is freely granted to anyone to modify     --
  6.     -- and/or redistribute this editor (ed.ex, syncolor.e). --
  7.     -- You may even sell it as it is, or with your          --
  8.     -- modifications.                                       --
  9.     ----------------------------------------------------------
  10.  
  11. without type_check -- makes it a bit faster
  12.  
  13. include graphics.e
  14. include get.e
  15. include file.e
  16.  
  17. constant TRUE = 1,
  18.      FALSE = 0
  19.  
  20. -------- user-modifiable parameters: 
  21.  
  22. constant PROG_INDENT = 4  -- tab width for editing program source files
  23.               -- (tab width is 8 for other files)
  24. constant E_FILES = {".e", ".ex", ".pro"}             -- Euphoria files
  25. constant PROG_FILES = E_FILES & {".c", ".h", ".bas"} -- program indent files
  26.  
  27. constant WANT_COLOR_SYNTAX  = TRUE -- FALSE if you don't want
  28.                    -- color syntax highlighting
  29.  
  30. constant WANT_AUTO_COMPLETE = TRUE -- FALSE if you don't want 
  31.                    -- auto-completion of Euphoria statements
  32.  
  33. constant HOT_KEYS = TRUE  -- FALSE if you want to hit Enter after each command
  34.  
  35. -- cursor style: 
  36. constant ED_CURSOR = THICK_UNDERLINE_CURSOR
  37.              -- UNDERLINE_CURSOR
  38.              -- HALF_BLOCK_CURSOR
  39.              -- BLOCK_CURSOR
  40.            
  41. -- number of lines on screen: (25,28,43,50)
  42. constant INITIAL_LINES = 25,  -- when editor starts up
  43.      FINAL_LINES = 25     -- when editor is finished
  44.  
  45. -- colors
  46. constant TOP_LINE_TEXT_COLOR = BLACK,
  47.      TOP_LINE_BACK_COLOR = BROWN, 
  48.      TOP_LINE_DIM_COLOR = BLUE,
  49.      BACKGROUND_COLOR = WHITE
  50.  
  51. -- colors needed by syncolor.e:
  52. -- Adjust to suit your monitor and your taste.
  53. global constant NORMAL_COLOR = BLACK,   -- GRAY might look better
  54.         COMMENT_COLOR = RED,
  55.         KEYWORD_COLOR = BLUE,
  56.         BUILTIN_COLOR = MAGENTA,
  57.         STRING_COLOR = GREEN,   -- BROWN might look better
  58.         BRACKET_COLOR = {NORMAL_COLOR, YELLOW, BRIGHT_WHITE, 
  59.                  BRIGHT_BLUE, CYAN, GREEN}
  60.  
  61. constant CONTROL_CHAR = 254 -- change funny control chars to this
  62.  
  63. -------- end of user-modifiable parameters 
  64.  
  65.  
  66. -- output device:
  67. global constant SCREEN = 1
  68.  
  69. constant SCREEN_WIDTH = 80
  70.  
  71. global constant BLANK_LINE = repeat(' ', SCREEN_WIDTH)
  72.  
  73. -- special input characters
  74. constant ESCAPE = 27,
  75.      CR = 13,
  76.      BS = 8,
  77.      HOME = 327,
  78.      END = 335,
  79.      CONTROL_HOME = 375,
  80.      CONTROL_END = 373,
  81.      PAGE_UP = 329,
  82.      PAGE_DOWN = 337,
  83.      INSERT = 338,
  84.      DELETE = 339,
  85.      ARROW_LEFT = 331,
  86.      ARROW_RIGHT = 333,
  87.      CONTROL_ARROW_LEFT = 371,
  88.      CONTROL_ARROW_RIGHT = 372,
  89.      ARROW_UP = 328,
  90.      ARROW_DOWN = 336,
  91.      F1 = 315,
  92.      F10 = 324,
  93.      CONTROL_DELETE = 403, -- key for line-delete 
  94.                    -- (not available on some systems)
  95.      CONTROL_D      = 4    -- alternate key for line-delete  
  96.  
  97. constant CONTROL_CHARS = {ESCAPE, BS, DELETE, PAGE_UP, PAGE_DOWN,
  98.               INSERT, CONTROL_DELETE, CONTROL_D,
  99.               ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ARROW_DOWN,
  100.               CONTROL_ARROW_LEFT, CONTROL_ARROW_RIGHT,
  101.               HOME, END, CONTROL_HOME, CONTROL_END,
  102.               F1, F1+1, F1+2, F1+3, F1+4, F1+5,
  103.               F1+6, F1+7, F1+8, F10}
  104.  
  105. constant STANDARD_TAB_WIDTH = 8
  106.  
  107. constant MAX_WINDOWS = 10 -- F1..F10
  108.  
  109. type boolean(integer x)
  110.     return x = TRUE or x = FALSE
  111. end type
  112.  
  113. type natural(integer x)
  114.     return x >= 0
  115. end type
  116.  
  117. type positive_int(integer x)
  118.     return x >= 1
  119. end type
  120.  
  121. sequence buffer -- In-memory buffer where the file is manipulated.
  122. -- This is a sequence where each element is a sequence
  123. -- containing one line of text. Each line of text ends with '\n'
  124.  
  125. positive_int screen_length  -- number of lines on physical screen
  126.  
  127. positive_int window_base    -- location of first line of current window 
  128.                 -- (status line)
  129. window_base = 1
  130. positive_int window_length  -- number of lines of text in current window
  131.  
  132. sequence window_list -- state info for all windows
  133. window_list = {0}
  134.  
  135. sequence buffer_list -- all buffers
  136. buffer_list = {}
  137.  
  138. type window_id(integer x)
  139.     return x >= 1 and x <= length(window_list)
  140. end type
  141.  
  142. type buffer_id(integer x)
  143.     return x >= 0 and x <= length(buffer_list)
  144. end type
  145.  
  146. type window_line(integer x)
  147. -- a valid line in the current window
  148.     return x >= 1 and x <= window_length
  149. end type
  150.  
  151. type screen_col(integer x)
  152. -- a valid column on the screen
  153.     return x >= 1 and x <= SCREEN_WIDTH
  154. end type
  155.  
  156. type buffer_line(integer x)
  157. -- a valid buffer line
  158.     return (x >= 1 and x <= length(buffer)) or x = 1
  159. end type
  160.  
  161. type char(integer x)
  162. -- a character
  163.     return x >= 0 and x <= 511
  164. end type
  165.  
  166. type extended_char(integer x)
  167.     return char(x) or x = -1
  168. end type
  169.  
  170. type file_number(integer x)
  171.     return x >= -1
  172. end type
  173.  
  174. sequence file_name   -- name of the file that we are editing
  175.  
  176. -- These are the critical state variables that all editing operations
  177. -- must update:
  178. buffer_line  b_line  -- current line in buffer
  179. positive_int b_col   -- current character within line in buffer
  180. window_line  s_line  -- line on screen corresponding to b_line
  181. screen_col   s_col   -- column on screen corresponding to b_col
  182.  
  183. boolean stop         -- indicates when to stop processing current buffer
  184.  
  185. sequence kill_buffer -- kill buffer of deleted lines or characters
  186. kill_buffer = {}
  187.  
  188. boolean adding_to_kill -- TRUE if still accumulating deleted lines/chars
  189.  
  190. boolean multi_color   -- use colors for keywords etc.
  191. boolean auto_complete -- perform auto completion of statements
  192. boolean dot_e         -- TRUE if this is a .e/.ex file
  193. boolean modified      -- TRUE if file has been modified
  194.              
  195. atom buffer_version,    -- version of buffer contents
  196.      my_buffer_version  -- last version used by current window
  197. boolean control_chars    -- binary file - view but don't save
  198.  
  199. natural start_line, start_col
  200.  
  201. sequence error_message
  202.  
  203. sequence config -- video configuration
  204.  
  205. window_id window_number -- current active window
  206. window_number = 1
  207.  
  208. buffer_id buffer_number -- current active buffer
  209. buffer_number = 0
  210.  
  211. procedure delay(atom n)
  212. -- an n second pause while a message is on the screen
  213.     atom t
  214.  
  215.     t = time()
  216.     while time() < t + n do
  217.     end while
  218. end procedure
  219.  
  220. procedure set_modified()
  221. -- buffer differs from file
  222.     modified = TRUE
  223.     cursor(NO_CURSOR) -- hide cursor while we update the screen
  224.     buffer_version = buffer_version + 1
  225. end procedure
  226.  
  227. procedure clear_modified()
  228. -- buffer is now same as file
  229.     modified = FALSE
  230. end procedure
  231.  
  232. function key_gets(sequence hot_keys)
  233. -- return input string from keyboard
  234. -- a bit more user-friendly than gets(0)
  235.    sequence input_string
  236.    integer line, init_column, column, char
  237.    sequence cursor
  238.    
  239.    if not HOT_KEYS then
  240.     hot_keys = ""
  241.    end if
  242.    cursor = get_position()
  243.    line = cursor[1]
  244.    init_column = cursor[2]
  245.    column = init_column
  246.    input_string = ""
  247.    while TRUE do
  248.     char = wait_key()
  249.     
  250.     if char = CR then
  251.         exit
  252.         
  253.     elsif char = BS or char = ARROW_LEFT then
  254.         if column > init_column then
  255.         column = column - 1
  256.         position(line, column)
  257.         puts(SCREEN, ' ')
  258.         position(line, column)
  259.         input_string = input_string[1..length(input_string)-1]
  260.         end if
  261.         
  262.     elsif char >= 32 or char = '\t' then
  263.         if column < SCREEN_WIDTH then
  264.         if char = '\t' then
  265.             puts(SCREEN, ' ')
  266.         else
  267.             puts(SCREEN, char)
  268.         end if
  269.         column = column + 1
  270.         if column - init_column > length(input_string) then
  271.             input_string = append(input_string, char)
  272.             if column = init_column + 1 and find(char, hot_keys) then
  273.             exit
  274.             end if
  275.         else
  276.             input_string[column - init_column] = char
  277.         end if
  278.         end if
  279.     end if
  280.     end while
  281.     return input_string
  282. end function
  283.  
  284. procedure set_position(natural window_line, positive_int column)
  285. -- convert relative position within window to absolute screen position
  286. -- window_line 0 is status line, window_line 1 is first line of text
  287.     position(window_base + window_line, column)
  288. end procedure
  289.  
  290. natural edit_tab_width 
  291.  
  292. function tab(natural tab_width, positive_int pos)
  293. -- compute new column position after a tab
  294.     return (floor((pos - 1) / tab_width) + 1) * tab_width + 1
  295. end function
  296.  
  297. function expand_tabs(natural tab_width, sequence line)
  298. -- replace tabs by blanks in a line of text
  299.     natural tab_pos, column, ntabs
  300.  
  301.     column = 1
  302.     while TRUE do
  303.     tab_pos = find('\t', line[column..length(line)])
  304.     if tab_pos = 0 then
  305.         -- no more tabs
  306.         return line
  307.     else
  308.         tab_pos = tab_pos + column - 1
  309.     end if
  310.     column = tab(tab_width, tab_pos)
  311.     ntabs = 1
  312.     while line[tab_pos+ntabs] = '\t' do
  313.         ntabs = ntabs + 1
  314.         column = column + tab_width
  315.     end while
  316.     -- replace consecutive tabs by blanks
  317.     line = line[1..tab_pos-1] & 
  318.            repeat(' ', column - tab_pos) &
  319.            line[tab_pos+ntabs..length(line)]            
  320.     end while
  321. end function
  322.  
  323. function indent_tabs(natural tab_width, sequence line)
  324. -- replace leading blanks of a line with tabs
  325.     natural i, blanks
  326.  
  327.     if length(line) < tab_width then
  328.     return line
  329.     end if
  330.     i = 1
  331.     while line[i] = ' ' do
  332.     i = i + 1
  333.     end while    
  334.     blanks = i - 1    
  335.     return repeat('\t', floor(blanks / tab_width)) & 
  336.        BLANK_LINE[1..remainder(blanks, tab_width)] &
  337.        line[i..length(line)]
  338. end function
  339.  
  340. function convert_tabs(natural old_width, natural new_width, sequence line)
  341. -- retabulate a line for a new tab size
  342.     if old_width = new_width then
  343.     return line
  344.     end if
  345.     return indent_tabs(new_width, expand_tabs(old_width, line))
  346. end function
  347.  
  348. -- color display of lines
  349. include syncolor.e
  350.  
  351. procedure reverse_video()
  352. -- start inverse video
  353.     text_color(TOP_LINE_TEXT_COLOR)
  354.     bk_color(TOP_LINE_BACK_COLOR)
  355. end procedure
  356.  
  357. procedure normal_video()
  358. -- end inverse video
  359.     text_color(NORMAL_COLOR)
  360.     bk_color(BACKGROUND_COLOR)
  361. end procedure
  362.  
  363. procedure ClearLine(window_line sline)
  364. -- clear the current line on screen
  365.     scroll(1, window_base + sline, window_base + sline)
  366. end procedure
  367.  
  368. procedure ClearWindow()
  369. -- clear the current window
  370.     scroll(window_length, window_base+1, window_base+window_length)
  371. end procedure
  372.  
  373. procedure ScrollUp(positive_int top, positive_int bottom)
  374. -- move text up one line on screen
  375.     scroll(+1, window_base + top, window_base + bottom)
  376. end procedure
  377.  
  378. procedure ScrollDown(positive_int top, positive_int bottom)
  379. -- move text down one line on screen
  380.     scroll(-1, window_base + top, window_base + bottom)
  381. end procedure
  382.  
  383. procedure DisplayLine(buffer_line bline, window_line sline, boolean all_clear)
  384. -- display a buffer line on a given line on the screen
  385.     sequence this_line
  386.     natural last
  387.     
  388.     this_line = expand_tabs(edit_tab_width, buffer[bline])
  389.     last = length(this_line) - 1
  390.     set_position(sline, 1)
  391.     if multi_color then
  392.     -- color display
  393.     DisplayColorLine(this_line)
  394.     else
  395.     -- monochrome display
  396.     puts(SCREEN, this_line[1..last])
  397.     end if
  398.     if last > SCREEN_WIDTH then
  399.     -- line too long 
  400.     set_position(sline, SCREEN_WIDTH)
  401.     text_color(BACKGROUND_COLOR)
  402.     bk_color(NORMAL_COLOR)
  403.     puts(SCREEN, this_line[SCREEN_WIDTH])
  404.     normal_video()
  405.     elsif not all_clear then
  406.     puts(SCREEN, BLANK_LINE)
  407.     end if
  408. end procedure
  409.  
  410. procedure DisplayWindow(positive_int bline, window_line sline)
  411. -- print a series of buffer lines, starting at sline on screen
  412. -- and continue until the end of screen, or end of buffer
  413.     boolean all_clear
  414.     
  415.     if sline = 1 then
  416.     ClearWindow()
  417.     all_clear = TRUE
  418.     else
  419.     all_clear = FALSE
  420.     end if
  421.  
  422.     for b = bline to length(buffer) do
  423.     DisplayLine(b, sline, all_clear)
  424.     if sline = window_length then
  425.         return
  426.     else
  427.         sline = sline + 1
  428.     end if
  429.     end for
  430.     -- blank any remaining screen lines after end of file
  431.     for s = sline to window_length do
  432.     ClearLine(s)
  433.     end for
  434. end procedure
  435.  
  436. function clean(sequence line)
  437. -- replace control characters with a graphics character
  438.     integer c
  439.     
  440.     for i = 1 to length(line) do
  441.     c = line[i]
  442.     if c < 14 then
  443.         if c != '\n' then
  444.         if c != '\t' then
  445.             line[i] = CONTROL_CHAR
  446.             control_chars = TRUE
  447.         end if
  448.         end if
  449.     end if
  450.     end for
  451.     if line[length(line)] != '\n' then
  452.     line = line & '\n'
  453.     end if
  454.     return line
  455. end function
  456.  
  457. function add_line(file_number file_no)
  458. -- add a new line to the buffer
  459.     object line
  460.  
  461.     line = gets(file_no)
  462.     
  463.     if atom(line) then
  464.     -- end of file
  465.     return FALSE 
  466.     end if
  467.     
  468.     line = convert_tabs(STANDARD_TAB_WIDTH, edit_tab_width, clean(line))
  469.     buffer = append(buffer, line)
  470.     return TRUE
  471. end function
  472.  
  473. procedure new_buffer()
  474. -- make room for a new (empty) buffer
  475.     buffer_list = buffer_list & 0 -- place holder for new buffer
  476.     buffer_number = length(buffer_list) 
  477.     buffer = {}
  478. end procedure
  479.  
  480. procedure read_file(file_number file_no)
  481. -- read the entire file into buffer variable
  482.     
  483.     -- read and immediately display the first screenful
  484.     for i = 1 to window_length do
  485.     if not add_line(file_no) then
  486.         exit
  487.     end if
  488.     end for
  489.     DisplayWindow(1, 1)
  490.  
  491.     -- read the rest
  492.     while add_line(file_no) do
  493.     end while
  494.  
  495. end procedure
  496.  
  497. procedure set_top_line(sequence message)
  498. -- print message on top line
  499.     set_position(0, 1)
  500.     reverse_video()
  501.     puts(SCREEN, message & BLANK_LINE)
  502.     set_position(0, length(message)+1)
  503. end procedure
  504.  
  505. procedure save_file(sequence file_name)
  506. -- write buffer back into the disk file
  507.     file_number file_no
  508.     
  509.     if control_chars then
  510.     set_top_line("")
  511.     printf(SCREEN, "%s: control chars were changed to " & CONTROL_CHAR &
  512.                " - save anyway? ", {file_name})
  513.     if not find('y', key_gets("yn")) then
  514.         stop = FALSE
  515.         return
  516.     end if
  517.     end if
  518.     set_top_line("")
  519.     file_no = open(file_name, "w")
  520.     if file_no = -1 then
  521.     printf(SCREEN, "Can't save %s - write permission denied", 
  522.           {file_name})
  523.     stop = FALSE
  524.     return
  525.     end if
  526.     printf(SCREEN, "saving %s ... ", {file_name})
  527.     for i = 1 to length(buffer) do
  528.     puts(file_no, 
  529.          convert_tabs(edit_tab_width, STANDARD_TAB_WIDTH, buffer[i]))
  530.     end for
  531.     close(file_no)
  532.     puts(SCREEN, "ok")
  533.     clear_modified()
  534.     stop = TRUE
  535. end procedure
  536.  
  537. procedure arrow_right()
  538. -- action for right arrow key
  539.  
  540.     positive_int temp_col
  541.  
  542.     if b_col < length(buffer[b_line]) then
  543.     if buffer[b_line][b_col] = '\t' then
  544.         temp_col = tab(edit_tab_width, s_col)
  545.     else
  546.         temp_col = s_col + 1
  547.     end if
  548.     if temp_col > SCREEN_WIDTH then
  549.         return
  550.     end if
  551.     s_col = temp_col
  552.     b_col = b_col + 1
  553.     end if
  554. end procedure
  555.  
  556. procedure arrow_left()
  557. -- action for left arrow key
  558.  
  559.     positive_int old_b_col
  560.  
  561.     old_b_col = b_col
  562.     b_col = 1
  563.     s_col = 1
  564.     for i = 1 to old_b_col - 2 do
  565.     arrow_right()
  566.     end for
  567. end procedure
  568.     
  569. procedure skip_white()
  570. -- set cursor to first non-whitespace in line    
  571.     positive_int temp_col
  572.     
  573.     while find(buffer[b_line][b_col], " \t") do
  574.     temp_col = s_col
  575.     arrow_right()
  576.     if s_col = temp_col then
  577.         return -- can't move any further right
  578.     end if
  579.     end while
  580. end procedure
  581.  
  582. procedure goto_line(integer new_line, integer new_col)
  583. -- move to a specified line and column
  584. -- refresh screen if line is 0
  585.     integer new_s_line
  586.     boolean refresh
  587.  
  588.     if length(buffer) = 0 then
  589.     ClearWindow()
  590.     s_line = 1
  591.     s_col = 1
  592.     return
  593.     end if
  594.     if new_line = 0 then
  595.     new_line = b_line
  596.     refresh = TRUE
  597.     else
  598.     refresh = FALSE
  599.     end if
  600.     if new_line < 1 then
  601.     new_line = 1
  602.     elsif new_line > length(buffer) then
  603.     new_line = length(buffer)
  604.     end if
  605.     new_s_line = new_line - b_line + s_line
  606.     b_line = new_line
  607.     if not refresh and window_line(new_s_line) then
  608.     -- new line is on the screen
  609.     s_line = new_s_line
  610.     else
  611.     -- new line is off the screen, or refreshing
  612.     set_position(1, 1)
  613.     s_line = floor((window_length+1)/2)
  614.     if s_line > b_line or length(buffer) < window_length then
  615.         s_line = b_line
  616.     elsif b_line > length(buffer) - window_length + s_line then
  617.         s_line = window_length - (length(buffer) - b_line)
  618.     end if
  619.     DisplayWindow(b_line - s_line + 1, 1)
  620.     end if
  621.     b_col = 1
  622.     s_col = 1
  623.     set_position(s_line, s_col)
  624.     for i = 1 to new_col-1 do
  625.     arrow_right()
  626.     end for
  627. end procedure
  628.  
  629. function plain_text(char c)
  630. -- defines text for next_word, previous_word 
  631.     return (c >= '0' and c <= '9') or
  632.        (c >= 'A' and c <= 'Z') or
  633.        (c >= 'a' and c <= 'z') or
  634.        c = '_'
  635. end function
  636.  
  637. procedure next_word()
  638. -- move to start of next word in line
  639.     char c
  640.     positive_int col
  641.     
  642.     -- skip plain text
  643.     col = b_col
  644.     while TRUE do
  645.     c = buffer[b_line][col]
  646.     if not plain_text(c) then
  647.         exit
  648.     end if
  649.     col = col + 1
  650.     end while
  651.     
  652.     -- skip white-space and punctuation
  653.     while c != '\n' do
  654.     c = buffer[b_line][col]
  655.     if plain_text(c) then
  656.         exit
  657.     end if
  658.     col = col + 1
  659.     end while
  660.     goto_line(b_line, col)
  661. end procedure
  662.  
  663. procedure previous_word()
  664. -- move to start of previous word in line    
  665.     char c
  666.     natural col
  667.     
  668.     -- skip white-space & punctuation
  669.     col = b_col - 1
  670.     while col > 1 do
  671.     c = buffer[b_line][col]
  672.     if plain_text(c) then
  673.         exit
  674.     end if
  675.     col = col - 1
  676.     end while
  677.  
  678.     -- skip plain text
  679.     while col > 1 do
  680.     c = buffer[b_line][col-1]
  681.     if not plain_text(c) then
  682.         exit
  683.     end if
  684.     col = col - 1
  685.     end while
  686.  
  687.     goto_line(b_line, col)
  688. end procedure
  689.  
  690.  
  691. procedure arrow_up()
  692. -- action for up arrow key
  693.  
  694.     b_col = 1
  695.     s_col = 1
  696.     if b_line > 1 then
  697.     b_line = b_line - 1
  698.     if s_line > 1 then
  699.         s_line = s_line - 1
  700.     else
  701.         -- move all lines down, display new line at top
  702.         ScrollDown(1, window_length)
  703.         DisplayLine(b_line, 1, TRUE)
  704.         set_position(1, 1)
  705.         s_line = 1
  706.     end if
  707.     skip_white()
  708.     end if
  709. end procedure
  710.  
  711. procedure arrow_down()
  712. -- action for down arrow key
  713.     b_col = 1
  714.     s_col = 1
  715.     if b_line < length(buffer) then
  716.     b_line = b_line + 1
  717.     if s_line < window_length then
  718.         s_line = s_line + 1
  719.     else
  720.         -- move all lines up, display new line at bottom
  721.         ScrollUp(1, window_length)
  722.         DisplayLine(b_line, window_length, TRUE)
  723.     end if
  724.     skip_white()
  725.     end if
  726. end procedure
  727.  
  728. function numeric(sequence string)
  729. -- convert digit string to an integer
  730.     atom n
  731.  
  732.     n = 0
  733.     for i = 1 to length(string) do
  734.     if string[i] >= '0' and string[i] <= '9' then
  735.         n = n * 10 + string[i] - '0'
  736.         if not integer(n) then
  737.         return 0
  738.         end if
  739.     else
  740.         exit
  741.     end if
  742.     end for
  743.     return n
  744. end function
  745.  
  746. procedure page_down()
  747. -- action for page-down key
  748.     buffer_line prev_b_line
  749.  
  750.     if length(buffer) <= window_length then
  751.     return
  752.     end if
  753.     prev_b_line = b_line
  754.     b_col = 1
  755.     s_col = 1
  756.     if b_line + window_length + window_length - s_line <= length(buffer) then
  757.     b_line = b_line + window_length
  758.     else
  759.     b_line = length(buffer) - (window_length - s_line)
  760.     end if
  761.     if b_line != prev_b_line then
  762.     DisplayWindow(b_line - s_line + 1, 1)
  763.     end if
  764. end procedure
  765.  
  766. procedure page_up()
  767. -- action for page-up key
  768.     buffer_line prev_b_line
  769.  
  770.     if length(buffer) <= window_length then
  771.     return
  772.     end if
  773.     prev_b_line = b_line
  774.     b_col = 1
  775.     s_col = 1
  776.     if b_line - window_length >= s_line then
  777.     b_line = b_line - window_length
  778.     else
  779.     b_line = s_line
  780.     end if
  781.     if b_line != prev_b_line then
  782.     DisplayWindow(b_line - s_line + 1, 1)
  783.     end if
  784. end procedure
  785.  
  786. procedure set_f_line(natural w, sequence comment)
  787. -- show F-key & file_name
  788.     sequence f_key, text
  789.     
  790.     if length(window_list) = 1 then
  791.     f_key = ""
  792.     elsif w = 10 then
  793.     f_key = "F10: "
  794.     else
  795.     f_key = 'F' & ('0' + w) & ": "
  796.     end if
  797.     set_top_line("")
  798.     puts(SCREEN, ' ' & f_key & file_name & comment)
  799.     text = "Esc for commands"
  800.     set_position(0, SCREEN_WIDTH - length(text))
  801.     puts(SCREEN, text)
  802. end procedure
  803.  
  804. constant W_BUFFER_NUMBER = 1,
  805.      W_MY_BUFFER_VERSION = 2,
  806.      W_WINDOW_BASE = 3,
  807.      W_WINDOW_LENGTH = 4,
  808.      W_B_LINE = 10
  809.  
  810. procedure save_state()
  811. -- save current state variables for a window
  812.     window_list[window_number] = {buffer_number, buffer_version, window_base, 
  813.                   window_length, auto_complete, multi_color, 
  814.                   dot_e, control_chars, file_name, b_line, 
  815.                   b_col, s_line, s_col, edit_tab_width}
  816.     buffer_list[buffer_number] = {buffer, modified, buffer_version}
  817. end procedure
  818.  
  819. procedure restore_state(window_id w)
  820. -- restore state variables for a window
  821.     sequence state
  822.     sequence buffer_info
  823.     
  824.     -- set up new buffer
  825.     state = window_list[w]
  826.     window_number = w
  827.     buffer_number =  state[W_BUFFER_NUMBER]
  828.     buffer_info = buffer_list[buffer_number]
  829.     buffer = buffer_info[1]
  830.     modified = buffer_info[2]
  831.     buffer_version = buffer_info[3]
  832.     buffer_list[buffer_number] = 0 -- save space
  833.     
  834.     -- restore other variables
  835.     my_buffer_version = state[2]
  836.     window_base = state[3]
  837.     window_length = state[4]
  838.     auto_complete = state[5]
  839.     multi_color = state[6]
  840.     dot_e = state[7]
  841.     control_chars = state[8]
  842.     file_name = state[9]
  843.     edit_tab_width = state[14]
  844.     set_f_line(w, "")
  845.     normal_video()
  846.     if buffer_version != my_buffer_version then
  847.     -- buffer has changed since we were last in this window
  848.     -- or window size has changed
  849.     if state[W_B_LINE] > length(buffer) then
  850.         if length(buffer) = 0 then
  851.         b_line = 1
  852.         else
  853.         b_line = length(buffer)
  854.         end if
  855.     else
  856.         b_line = state[W_B_LINE]
  857.     end if
  858.     goto_line(0, 1)
  859.     else
  860.     b_line = state[W_B_LINE]
  861.     b_col = state[11]
  862.     s_line = state[12]
  863.     s_col = state[13]
  864.     set_position(s_line, s_col)
  865.     end if
  866. end procedure
  867.  
  868. procedure refresh_other_windows(positive_int w)
  869. -- redisplay all windows except w
  870.     
  871.     normal_video()
  872.     for i = 1 to length(window_list) do
  873.     if i != w then
  874.         restore_state(i)
  875.         set_f_line(i, "")
  876.         normal_video()
  877.         goto_line(0, b_col)
  878.         save_state()
  879.     end if
  880.     end for
  881. end procedure
  882.  
  883. procedure set_window_size()
  884. -- set sizes for windows
  885.     natural nwindows, lines, base, size
  886.     
  887.     nwindows = length(window_list)
  888.     lines = screen_length - nwindows
  889.     base = 1
  890.     for i = 1 to length(window_list) do
  891.     size = floor(lines / nwindows)
  892.     window_list[i][W_WINDOW_BASE] = base
  893.     window_list[i][W_WINDOW_LENGTH] = size
  894.     window_list[i][W_MY_BUFFER_VERSION] = -1 -- force redisplay
  895.     base = base + size + 1
  896.     nwindows = nwindows - 1
  897.     lines = lines - size
  898.     end for
  899. end procedure
  900.  
  901. procedure clone_window()
  902. -- set up a new window that is a clone of the current window
  903. -- save state of current window
  904.     window_id w
  905.     
  906.     if length(window_list) >= MAX_WINDOWS then
  907.     return
  908.     end if
  909.     save_state()
  910.     -- create a place for new window
  911.     window_list = window_list[1..window_number] & 
  912.           {window_list[window_number]} &  -- the new clone window
  913.           window_list[window_number+1..length(window_list)]
  914.     w = window_number + 1
  915.     set_window_size()
  916.     refresh_other_windows(w)
  917.     restore_state(w) 
  918. end procedure
  919.  
  920. procedure switch_window(integer new_window_number)
  921. -- switch context to a new window on the screen
  922.     
  923.     if new_window_number != window_number then
  924.     save_state()
  925.     restore_state(new_window_number)
  926.     end if
  927. end procedure
  928.  
  929. function delete_window()
  930. -- delete the current window    
  931.     boolean buff_in_use
  932.     
  933.     buffer_list[buffer_number] = {buffer, modified, buffer_version}
  934.     window_list = window_list[1..window_number-1] & 
  935.           window_list[window_number+1..length(window_list)]
  936.     buff_in_use = FALSE
  937.     for i = 1 to length(window_list) do
  938.     if window_list[i][W_BUFFER_NUMBER] = buffer_number then
  939.         buff_in_use = TRUE
  940.         exit
  941.     end if
  942.     end for 
  943.     if not buff_in_use then
  944.     buffer_list[buffer_number] = 0 -- discard the buffer
  945.     end if
  946.     if length(window_list) = 0 then
  947.     return TRUE
  948.     end if
  949.     set_window_size()
  950.     refresh_other_windows(1)
  951.     window_number = 1
  952.     restore_state(window_number)
  953.     set_position(s_line, s_col)
  954.     return FALSE
  955. end function
  956.  
  957. procedure new_screen_length()
  958. -- set new number of lines on screen
  959.     natural nlines
  960.     window_id w
  961.     
  962.     set_top_line("How many lines on screen? (25, 28, 43, 50) ")
  963.     nlines = numeric(key_gets(""))
  964.     if nlines then
  965.     screen_length = text_rows(nlines)
  966.     if screen_length != nlines then
  967.         sound(500)
  968.     end if
  969.     w = window_number
  970.     save_state()
  971.     set_window_size()
  972.     refresh_other_windows(w)
  973.     restore_state(w)
  974.     if screen_length != nlines then
  975.         sound(0)
  976.     end if
  977.     end if
  978. end procedure
  979.  
  980. -- searching/replacing variables
  981. boolean searching, replacing, match_case
  982. searching = FALSE
  983. replacing = FALSE
  984. match_case = TRUE
  985.  
  986. sequence find_string -- current (default) string to look for
  987. find_string = ""
  988.  
  989. sequence replace_string -- current (default) string to replace with
  990. replace_string = ""
  991.  
  992. procedure replace()
  993. -- replace find_string by replace_string
  994. -- we are currently positioned at the start of an occurrence of find_string
  995.     sequence line
  996.  
  997.     set_modified()
  998.     line = buffer[b_line]
  999.     line = line[1..b_col-1] & replace_string & line[b_col+length(find_string)..
  1000.                         length(line)]
  1001.     buffer[b_line] = line
  1002.     -- position at end of replacement string
  1003.     for i = 1 to length(replace_string)-1 do
  1004.     arrow_right()
  1005.     end for
  1006.     DisplayLine(b_line, s_line, FALSE)
  1007. end procedure
  1008.  
  1009. function lower(sequence s)
  1010. -- convert to lower case
  1011.     integer c
  1012.     for i = 1 to length(s) do
  1013.     c = s[i]
  1014.     if c >= 'A' then
  1015.         if c <= 'Z' then
  1016.         s[i] = c + 'a' - 'A' 
  1017.         end if
  1018.     end if
  1019.     end for
  1020.     return s
  1021. end function
  1022.  
  1023. function alphabetic(object s)
  1024. -- does s contain alphabetic characters?
  1025.     return find(TRUE, (s >= 'A' and s <= 'Z') or
  1026.               (s >= 'a' and s <= 'z')) 
  1027. end function
  1028.  
  1029. function case_match(sequence string, sequence text)
  1030. -- Find string in text with
  1031. -- either case-sensitive or non-case-sensitive comparison
  1032.     if match_case then
  1033.     return match(string, text)
  1034.     else
  1035.     return match(lower(string), lower(text))
  1036.     end if
  1037. end function
  1038.  
  1039. function search(boolean continue)
  1040. -- find a string from here to the end of the file
  1041. -- return TRUE if string is found
  1042.     natural col
  1043.     sequence temp_find_string, temp_replace_string
  1044.     sequence pos
  1045.     
  1046.     set_top_line("")
  1047.     if length(buffer) = 0 then
  1048.     puts(SCREEN, "buffer empty")
  1049.     return FALSE
  1050.     end if
  1051.     if length(find_string) = 0 then
  1052.     puts(SCREEN, "searching for:")
  1053.     else
  1054.     printf(SCREEN, "searching for \"%s\":", {find_string})
  1055.     end if
  1056.     if not continue then
  1057.     pos = get_position()
  1058.     temp_find_string = key_gets("")
  1059.     if length(temp_find_string) > 0 then
  1060.         -- new string typed in
  1061.         find_string = temp_find_string
  1062.         if alphabetic(find_string) then
  1063.         set_position(0, pos[2]+length(temp_find_string)+3)
  1064.         puts(SCREEN, "match case? n")
  1065.         pos = get_position()
  1066.         set_position(0, pos[2] - 1)
  1067.         match_case = find('y', key_gets(""))
  1068.         end if
  1069.     end if
  1070.     if replacing then
  1071.         set_top_line("")
  1072.         if length(replace_string) = 0 then
  1073.         puts(SCREEN, "replace with:")
  1074.         else
  1075.         printf(SCREEN, "replace with \"%s\":", {replace_string})
  1076.         end if
  1077.         temp_replace_string = key_gets("")
  1078.         if length(temp_replace_string) > 0 then
  1079.         replace_string = temp_replace_string
  1080.         end if
  1081.     end if
  1082.     end if
  1083.  
  1084.     normal_video()
  1085.     if length(find_string) = 0 then
  1086.     return FALSE
  1087.     end if
  1088.     col = case_match(find_string, buffer[b_line][b_col+1..length(buffer[b_line])])
  1089.     if col and s_col < SCREEN_WIDTH then
  1090.     -- found it on this line after current position
  1091.     for i = 1 to col do
  1092.         arrow_right()
  1093.     end for
  1094.     if replacing then
  1095.         replace()
  1096.     end if
  1097.     return TRUE
  1098.     else
  1099.     -- check lines following this one
  1100.     for b = b_line+1 to length(buffer) do
  1101.         col = case_match(find_string, buffer[b])
  1102.         if col then
  1103.         goto_line(b, 1)
  1104.         for i = 1 to col - 1 do
  1105.            arrow_right()
  1106.         end for
  1107.         if replacing and s_col < SCREEN_WIDTH then
  1108.             replace()
  1109.         end if
  1110.         set_top_line("")
  1111.         printf(SCREEN, "searching for \"%s\":", {find_string})
  1112.         return TRUE
  1113.         end if
  1114.     end for
  1115.     set_top_line("")
  1116.     printf(SCREEN, "\"%s\" not found", {find_string})
  1117.     if alphabetic(find_string) then
  1118.         if match_case then
  1119.         puts(SCREEN, "  (case must match)")
  1120.         else
  1121.         puts(SCREEN, "  (any case)")
  1122.         end if
  1123.     end if
  1124.     end if
  1125.     return FALSE
  1126. end function
  1127.  
  1128. procedure show_message()
  1129. -- display error message from ex.err
  1130.     if length(error_message) > 0 then
  1131.     set_top_line("")
  1132.     puts(SCREEN, error_message)
  1133.     normal_video()
  1134.     end if
  1135.     set_position(s_line, s_col)
  1136. end procedure
  1137.  
  1138. procedure set_err_pointer()
  1139. -- set cursor at point of error 
  1140.  
  1141.     for i = 1 to SCREEN_WIDTH do
  1142.     if s_col >= start_col then
  1143.         exit
  1144.     end if
  1145.     arrow_right()
  1146.     end for
  1147. end procedure
  1148.  
  1149. function delete_trailing_white(sequence name)
  1150. -- get rid of blanks, tabs, newlines at end of string
  1151.     while length(name) > 0 do
  1152.     if find(name[length(name)], "\n\r\t ") then
  1153.         name = name[1..length(name)-1]
  1154.     else
  1155.         exit
  1156.     end if
  1157.     end while
  1158.     return name
  1159. end function
  1160.  
  1161. function get_err_line()
  1162. -- try to get file name & line number from ex.err
  1163. -- returns file_name, sets start_line, start_col, error_message
  1164.  
  1165.     file_number err_file
  1166.     sequence file_name
  1167.     sequence err_lines
  1168.     object temp_line
  1169.     natural colon_pos
  1170.  
  1171.     err_file = open("ex.err", "r")
  1172.     if err_file = -1 then
  1173.     error_message = ""
  1174.     else
  1175.     -- read the top of the ex.err error message file
  1176.     err_lines = {}
  1177.     while length(err_lines) < 5 do
  1178.         temp_line = gets(err_file)
  1179.         if atom(temp_line) then
  1180.         exit
  1181.         end if
  1182.         err_lines = append(err_lines, temp_line)
  1183.     end while
  1184.     close(err_file)
  1185.     -- look for file name, line, column and error message
  1186.     if length(err_lines) > 0 then
  1187.         if sequence(err_lines[1]) then
  1188.         colon_pos = match(".e", lower(err_lines[1]))
  1189.         if colon_pos then
  1190.             if find(err_lines[1][colon_pos+2],{'x', 'X'}) then
  1191.             colon_pos = colon_pos + 1
  1192.             end if
  1193.             file_name = err_lines[1][1..colon_pos+1]
  1194.             start_line = numeric(err_lines[1][colon_pos+3..
  1195.                               length(err_lines[1])])
  1196.             error_message = delete_trailing_white(err_lines[2])
  1197.             if length(err_lines) > 3 then
  1198.             start_col = find('^', expand_tabs(STANDARD_TAB_WIDTH, 
  1199.                           err_lines[length(err_lines)-1]))
  1200.             end if
  1201.             return file_name
  1202.         end if
  1203.         end if
  1204.     end if
  1205.     end if
  1206.     return ""
  1207. end function
  1208.  
  1209. function last_use()
  1210. -- return TRUE if current buffer 
  1211. -- is only referenced by the current window
  1212.     natural count
  1213.     
  1214.     count = 0
  1215.     for i = 1 to length(window_list) do
  1216.     if window_list[i][W_BUFFER_NUMBER] = buffer_number then
  1217.         count = count + 1
  1218.         if count = 2 then
  1219.         return FALSE
  1220.         end if
  1221.     end if
  1222.     end for
  1223.     return TRUE
  1224. end function
  1225.  
  1226. procedure shell(sequence command, boolean wait)
  1227. -- run a DOS command
  1228.     window_id w
  1229.     
  1230.     bk_color(BLACK)
  1231.     text_color(WHITE)
  1232.     clear_screen()
  1233.     system(command, wait)
  1234.     normal_video()
  1235.     while get_key() != -1 do
  1236.     -- clear the keyboard buffer
  1237.     end while
  1238.     w = window_number
  1239.     save_state()
  1240.     refresh_other_windows(w)
  1241.     restore_state(w)
  1242. end procedure
  1243.  
  1244. procedure first_bold(sequence string)
  1245. -- highlight first char
  1246.     text_color(TOP_LINE_TEXT_COLOR)
  1247.     puts(SCREEN, string[1])
  1248.     text_color(TOP_LINE_DIM_COLOR)
  1249.     puts(SCREEN, string[2..length(string)])
  1250. end procedure
  1251.  
  1252. procedure get_escape(boolean help)
  1253. -- process escape command
  1254.     sequence command, answer, temp_name
  1255.     natural line
  1256.     object dos_command
  1257.  
  1258.     cursor(ED_CURSOR)
  1259.  
  1260.     set_top_line("")
  1261.     if help then
  1262.     command = "h"
  1263.     else
  1264.     first_bold("help  ")
  1265.     first_bold("clone  ")
  1266.     first_bold("quit|")
  1267.     first_bold("save|")
  1268.     first_bold("write|")
  1269.     first_bold("new  ")
  1270.     if dot_e then
  1271.         first_bold("ex|")
  1272.     end if
  1273.     first_bold("dos  ")
  1274.     first_bold("find|")
  1275.     first_bold("replace  ")
  1276.     first_bold("lines  ")
  1277.     text_color(TOP_LINE_TEXT_COLOR)
  1278.     puts(SCREEN, "ddd  CR: ")
  1279.     command = key_gets("hcqswnedfrl") & ' '
  1280.     end if
  1281.  
  1282.     if command[1] = 'f' then
  1283.     replacing = FALSE
  1284.     searching = search(FALSE)
  1285.  
  1286.     elsif command[1] = 'r' then
  1287.     replacing = TRUE
  1288.     searching = search(FALSE)
  1289.  
  1290.     elsif command[1] = 'q' then
  1291.     if modified and last_use() then
  1292.         set_top_line("quit without saving changes? ")
  1293.         if find('y', key_gets("yn")) then
  1294.         stop = delete_window()
  1295.         end if
  1296.     else
  1297.         stop = delete_window()
  1298.     end if
  1299.     
  1300.     elsif command[1] = 'c' then
  1301.     clone_window()
  1302.     
  1303.     elsif command[1] = 'n' then
  1304.     if modified and last_use() then
  1305.         set_top_line("")
  1306.         printf(SCREEN, "save changes to %s? ", {file_name})
  1307.         if find('y', key_gets("yn")) then
  1308.         save_file(file_name)
  1309.         end if
  1310.     end if
  1311.     save_state()
  1312.     set_top_line("new file name: ")
  1313.     temp_name = delete_trailing_white(key_gets(""))
  1314.     if length(temp_name) != 0 then
  1315.         file_name = temp_name
  1316.         stop = TRUE
  1317.     end if
  1318.  
  1319.     elsif command[1] = 'w' then
  1320.     save_file(file_name)
  1321.     stop = FALSE
  1322.  
  1323.     elsif command[1] = 's' then
  1324.     save_file(file_name)
  1325.     if stop then
  1326.         stop = delete_window()
  1327.     end if
  1328.  
  1329.     elsif command[1] = 'e' and dot_e then
  1330.     if modified then
  1331.         save_file(file_name)
  1332.         stop = FALSE
  1333.     end if
  1334.     -- execute the current file & return
  1335.     if sequence(dir("ex.err")) then
  1336.         system("del ex.err > NUL", 0)
  1337.     end if
  1338.     shell("ex " & file_name, TRUE)
  1339.     goto_line(0, b_col)
  1340.     if compare(file_name, get_err_line()) = 0 then
  1341.         goto_line(start_line, 1)
  1342.         set_err_pointer()
  1343.         show_message()
  1344.     end if
  1345.  
  1346.     elsif command[1] = 'd' then
  1347.     set_top_line("DOS command? ")
  1348.     dos_command = key_gets("")
  1349.     shell(dos_command, TRUE)
  1350.     goto_line(0, b_col) -- refresh screen
  1351.  
  1352.     elsif command[1] = 'h' then
  1353.     dos_command = getenv("EUDIR")
  1354.     if atom(dos_command) then
  1355.         -- Euphoria hasn't been installed yet 
  1356.         set_top_line("EUDIR not set. See install.doc")
  1357.     else    
  1358.         dos_command = "ed.bat " & dos_command & "\\DOC"
  1359.         if help then
  1360.         set_top_line(
  1361.         "That key does nothing - do you want to view the help text? ")
  1362.         answer = key_gets("yn") & ' '
  1363.         if answer[1] != 'n' and answer[1] != 'N' then
  1364.             answer = "e"
  1365.         end if
  1366.         else
  1367.         set_top_line("ed.doc, refman.doc, or library.doc? (e, r or l): ")
  1368.         answer = key_gets("erl") & ' '
  1369.         end if
  1370.         if answer[1] = 'r' then
  1371.         shell(dos_command & "\\REFMAN.DOC", FALSE)
  1372.         goto_line(0, b_col)
  1373.         elsif answer[1] = 'e' then
  1374.         shell(dos_command & "\\ED.DOC", FALSE)
  1375.         goto_line(0, b_col)
  1376.         elsif answer[1] = 'l' then
  1377.         shell(dos_command & "\\LIBRARY.DOC", FALSE)
  1378.         goto_line(0, b_col)
  1379.         else
  1380.         normal_video()
  1381.         end if
  1382.     end if
  1383.  
  1384.     elsif command[1] = 'l' then
  1385.     new_screen_length()
  1386.  
  1387.     elsif command[1] >= '0' and command[1] <= '9' then
  1388.     line = numeric(command)
  1389.     normal_video()
  1390.     goto_line(line, 1)
  1391.     if not buffer_line(line) then
  1392.         set_top_line("")
  1393.         printf(SCREEN, "lines are 1..%d", length(buffer))
  1394.     end if
  1395.  
  1396.     else
  1397.     set_top_line("")
  1398.     if length(buffer) = 0 then
  1399.         puts(SCREEN, "empty buffer")
  1400.     else
  1401.         printf(SCREEN, "%s line %d of %d, column %d of %d, ",
  1402.                {file_name, b_line, length(buffer), s_col,
  1403.             length(expand_tabs(edit_tab_width, buffer[b_line]))-1})
  1404.         if modified then
  1405.         puts(SCREEN, "modified")
  1406.         else
  1407.         puts(SCREEN, "not modified")
  1408.         end if
  1409.     end if
  1410.     end if
  1411.  
  1412.     normal_video()
  1413. end procedure
  1414.  
  1415. procedure insert(char key)
  1416. -- insert a character into the current line at the current position
  1417.  
  1418.     sequence tail
  1419.     positive_int new_col
  1420.  
  1421.     set_modified()
  1422.     tail = buffer[b_line][b_col..length(buffer[b_line])]
  1423.     if key = CR or key = '\n' then
  1424.     -- truncate this line and create a new line using tail
  1425.     buffer[b_line] = buffer[b_line][1..b_col-1] & '\n'
  1426.     
  1427.     -- make room for new line:
  1428.     buffer = append(buffer, 0)
  1429.     for i = length(buffer)-1 to b_line+1 by -1 do
  1430.         buffer[i+1] = buffer[i]
  1431.     end for
  1432.     
  1433.     -- store new line
  1434.     buffer[b_line+1] = tail
  1435.     
  1436.     if s_line = window_length then
  1437.         arrow_down()
  1438.         arrow_up()
  1439.     else
  1440.         ScrollDown(s_line+1, window_length)
  1441.     end if
  1442.     if window_length = 1 then
  1443.         arrow_down()
  1444.     else
  1445.         DisplayLine(b_line, s_line, FALSE)
  1446.         b_line = b_line + 1
  1447.         s_line = s_line + 1
  1448.         DisplayLine(b_line, s_line, FALSE)
  1449.     end if
  1450.     s_col = 1
  1451.     b_col = 1
  1452.     else
  1453.     if key = '\t' then
  1454.         new_col = tab(edit_tab_width, s_col)
  1455.     else
  1456.         new_col = s_col + 1
  1457.     end if
  1458.     if new_col > SCREEN_WIDTH then
  1459.         return
  1460.     else
  1461.         s_col = new_col
  1462.     end if
  1463.     buffer[b_line] = buffer[b_line][1..b_col-1] & key & tail
  1464.     DisplayLine(b_line, s_line, TRUE)
  1465.     b_col = b_col + 1
  1466.     end if
  1467.     set_position(s_line, s_col)
  1468. end procedure
  1469.  
  1470. procedure insert_string(sequence text)
  1471. -- insert a bunch of characters at the current position
  1472.     natural save_line, save_col
  1473.  
  1474.     save_line = b_line
  1475.     save_col = b_col
  1476.     for i = 1 to length(text) do
  1477.     if text[i] = CR or text[i] = '\n' then
  1478.         insert(text[i])
  1479.     else
  1480.         buffer[b_line] = buffer[b_line][1..b_col-1] & text[i] &
  1481.                  buffer[b_line][b_col..length(buffer[b_line])]
  1482.         b_col = b_col + 1
  1483.         if i = length(text) then
  1484.         DisplayLine(b_line, s_line, FALSE)
  1485.         end if
  1486.     end if
  1487.     end for
  1488.     goto_line(save_line, save_col)
  1489. end procedure
  1490.  
  1491. -- expandable words & corresponding text
  1492. constant expand_word = {"if", "for", "while", "elsif",
  1493.             "procedure", "type", "function"},
  1494.  
  1495.      expand_text = {" then",  "=  to  by  do",  " do",  " then",
  1496.             "()",  "()",  "()" 
  1497.                }
  1498.  
  1499. procedure try_auto_complete(char key)
  1500. -- check for a keyword that can be automatically completed
  1501.     sequence word, this_line, white_space, leading_white, begin
  1502.     natural first_non_blank, wordnum
  1503.  
  1504.     if key = ' ' then
  1505.     insert(key)
  1506.     end if
  1507.     this_line = buffer[b_line]
  1508.     white_space = this_line = ' ' or this_line = '\t'
  1509.     first_non_blank = find(0, white_space) -- there's always '\n' at end
  1510.     leading_white = this_line[1..first_non_blank - 1]         
  1511.     if auto_complete and first_non_blank < b_col - 2 then
  1512.     if not find(0, white_space[b_col..length(white_space)-1]) then
  1513.         word = this_line[first_non_blank..b_col - 1 - (key = ' ')]
  1514.         wordnum = find(word, expand_word)           
  1515.         
  1516.         if key = CR and compare(word, "else") = 0 then    
  1517.          leading_white = leading_white & '\t'
  1518.         
  1519.         elsif wordnum > 0 then
  1520.         sound(1000)
  1521.         -- expandable word (only word on line)
  1522.  
  1523.         begin = expand_text[wordnum] & CR & leading_white
  1524.         
  1525.         if compare(word, "elsif") = 0 then
  1526.             insert_string(begin & '\t')
  1527.            
  1528.         elsif find(word, {"function", "type"}) then
  1529.             insert_string(begin & CR & 
  1530.                   leading_white & "\treturn" & CR &
  1531.                   "end " & expand_word[wordnum])
  1532.         else
  1533.             insert_string(begin & '\t' & CR &
  1534.                   leading_white &
  1535.                   "end " & expand_word[wordnum])
  1536.         end if
  1537.         delay(0.07) -- or beep is too short
  1538.         sound(0)
  1539.         end if
  1540.     end if
  1541.     end if
  1542.     if key = CR then
  1543.     if b_col >= first_non_blank then
  1544.         buffer[b_line] = buffer[b_line][1..b_col-1] & leading_white &
  1545.                  buffer[b_line][b_col..length(buffer[b_line])]
  1546.         insert(CR)
  1547.         skip_white()
  1548.     else
  1549.         insert(CR)
  1550.     end if
  1551.     end if
  1552. end procedure
  1553.  
  1554. procedure insert_kill_buffer()
  1555. -- insert the kill buffer at the current position
  1556. -- kill buffer could be a sequence of lines or a sequence of characters
  1557.  
  1558.     if length(kill_buffer) = 0 then
  1559.     return
  1560.     end if
  1561.     if atom(kill_buffer[1]) then
  1562.     -- inserting a sequence of chars
  1563.     insert_string(kill_buffer)
  1564.     else
  1565.     -- inserting a sequence of lines
  1566.     set_modified()
  1567.     buffer = buffer[1..b_line - 1] &
  1568.          kill_buffer &
  1569.          buffer[b_line..length(buffer)]
  1570.     DisplayWindow(b_line, s_line)
  1571.     b_col = 1
  1572.     s_col = 1
  1573.     end if
  1574. end procedure
  1575.  
  1576. procedure delete_line(buffer_line dead_line)
  1577. -- delete a line from the buffer and update the display if necessary
  1578.  
  1579.     integer x
  1580.  
  1581.     set_modified()
  1582.     
  1583.     -- move up all lines coming after the dead line
  1584.     for i = dead_line to length(buffer)-1 do
  1585.     buffer[i] = buffer[i+1]
  1586.     end for
  1587.     buffer = buffer[1..length(buffer)-1]
  1588.     
  1589.     x = dead_line - b_line + s_line
  1590.     if window_line(x) then
  1591.     -- dead line is on the screen at line x
  1592.     ScrollUp(x, window_length)
  1593.     if length(buffer) - b_line >= window_length - s_line then
  1594.         -- show new line at bottom
  1595.         DisplayLine(b_line + window_length - s_line, window_length, TRUE)
  1596.     end if
  1597.     end if
  1598.     if b_line > length(buffer) then
  1599.     arrow_up()
  1600.     else
  1601.     b_col = 1
  1602.     s_col = 1
  1603.     end if
  1604.     adding_to_kill = TRUE
  1605. end procedure
  1606.  
  1607. procedure delete_char()
  1608. -- delete the character at the current position
  1609.     char dchar
  1610.     sequence head
  1611.     natural save_b_col
  1612.  
  1613.     set_modified()
  1614.     dchar = buffer[b_line][b_col]
  1615.     head = buffer[b_line][1..b_col - 1]
  1616.     if dchar = '\n' then
  1617.     if b_line < length(buffer) then
  1618.         -- join this line with the next one and delete the next one
  1619.         buffer[b_line] = head & buffer[b_line+1]
  1620.         DisplayLine(b_line, s_line, FALSE)
  1621.         save_b_col = b_col
  1622.         delete_line(b_line + 1)
  1623.         for i = 1 to save_b_col - 1 do
  1624.         arrow_right()
  1625.         end for
  1626.     else
  1627.         if length(buffer[b_line]) = 1 then
  1628.         delete_line(b_line)
  1629.         else
  1630.         arrow_left() -- a line must always end with \n
  1631.         end if
  1632.     end if
  1633.     else
  1634.     buffer[b_line] = head & buffer[b_line][b_col+1..length(buffer[b_line])]
  1635.     if length(buffer[b_line]) = 0 then
  1636.         delete_line(b_line)
  1637.     else
  1638.         DisplayLine(b_line, s_line, FALSE)
  1639.         if b_col > length(buffer[b_line]) then
  1640.         arrow_left()
  1641.         end if
  1642.     end if
  1643.     end if
  1644.     adding_to_kill = TRUE
  1645. end procedure
  1646.  
  1647. function good(extended_char key)
  1648. -- return TRUE if key should be processed
  1649.     if find(key, CONTROL_CHARS) then
  1650.     return TRUE
  1651.     elsif key >= ' ' then
  1652.     return TRUE
  1653.     elsif key = '\t' or key = CR then
  1654.     return TRUE
  1655.     else
  1656.     return FALSE
  1657.     end if
  1658. end function
  1659.  
  1660. procedure edit_file()
  1661. -- edit the file in buffer
  1662.     extended_char key
  1663.  
  1664.     if length(buffer) > 0 then
  1665.     if start_line > 0 then
  1666.         if start_line > length(buffer) then
  1667.         start_line = length(buffer)
  1668.         end if
  1669.         goto_line(start_line, 1)
  1670.         set_err_pointer()
  1671.         show_message()
  1672.     end if
  1673.     end if
  1674.     
  1675.     -- speed up keyboard repeat rate:
  1676.     -- system("mode con rate=30 delay=2", 2)
  1677.     
  1678.     cursor(ED_CURSOR)
  1679.     stop = FALSE
  1680.  
  1681.     while not stop do
  1682.  
  1683.     key = wait_key()
  1684.     
  1685.     if good(key) then
  1686.         -- normal key
  1687.  
  1688.         if key >= F1 and key <= F10 then
  1689.         if key < F1 + length(window_list) then
  1690.             switch_window(key - F1 + 1)
  1691.         else
  1692.             set_top_line("")
  1693.             printf(SCREEN, "F%d is not an active window", key - F1 + 1)
  1694.             normal_video()
  1695.         end if
  1696.         adding_to_kill = FALSE
  1697.         
  1698.         elsif length(buffer) = 0 and key != ESCAPE then
  1699.         -- empty buffer
  1700.         -- only allowed action is to insert something
  1701.         if key = INSERT or not find(key, CONTROL_CHARS) then
  1702.             -- initialize buffer
  1703.             buffer = {{'\n'}} -- one line with \n
  1704.             b_line = 1
  1705.             b_col = 1
  1706.             s_line = 1
  1707.             s_col = 1
  1708.             if key = INSERT then
  1709.             insert_kill_buffer()
  1710.             else
  1711.             insert(key)
  1712.             end if
  1713.             DisplayLine(1, 1, FALSE)
  1714.         end if
  1715.  
  1716.         elsif key = DELETE then
  1717.         if not adding_to_kill then
  1718.             kill_buffer = {buffer[b_line][b_col]}
  1719.         elsif sequence(kill_buffer[1]) then
  1720.             -- we were building up deleted lines,
  1721.             -- but now we'll switch to chars
  1722.             kill_buffer = {buffer[b_line][b_col]}
  1723.         else
  1724.             kill_buffer = append(kill_buffer, buffer[b_line][b_col])
  1725.         end if
  1726.         delete_char()
  1727.  
  1728.         elsif key = CONTROL_DELETE or key = CONTROL_D then
  1729.         if not adding_to_kill then
  1730.             kill_buffer = {buffer[b_line]}
  1731.         elsif atom(kill_buffer[1]) then
  1732.             -- we were building up deleted chars,
  1733.             -- but now we'll switch to lines
  1734.             kill_buffer = {buffer[b_line]}
  1735.         else
  1736.             kill_buffer = append(kill_buffer, buffer[b_line])
  1737.         end if
  1738.         delete_line(b_line)
  1739.  
  1740.         else
  1741.         if key = PAGE_DOWN then
  1742.             page_down()
  1743.  
  1744.         elsif key = PAGE_UP then
  1745.             page_up()
  1746.  
  1747.         elsif key = ARROW_LEFT then
  1748.             arrow_left()
  1749.  
  1750.         elsif key = ARROW_RIGHT then
  1751.             arrow_right()
  1752.  
  1753.         elsif key = CONTROL_ARROW_LEFT then
  1754.             previous_word()
  1755.             
  1756.         elsif key = CONTROL_ARROW_RIGHT then
  1757.             next_word()
  1758.             
  1759.         elsif key = ARROW_DOWN then
  1760.             arrow_down()
  1761.  
  1762.         elsif key = ARROW_UP then
  1763.             arrow_up()
  1764.  
  1765.         elsif key = ' ' then
  1766.             try_auto_complete(key)
  1767.  
  1768.         elsif key = INSERT then
  1769.             insert_kill_buffer()
  1770.  
  1771.         elsif key = BS then
  1772.             arrow_left()
  1773.             delete_char()
  1774.  
  1775.         elsif key = HOME then
  1776.             b_col = 1
  1777.             s_col = 1
  1778.         
  1779.         elsif key = END then
  1780.             goto_line(b_line, SCREEN_WIDTH)
  1781.         
  1782.         elsif key = CONTROL_HOME then
  1783.             goto_line(1, 1)
  1784.  
  1785.         elsif key = CONTROL_END then
  1786.             goto_line(length(buffer), SCREEN_WIDTH)
  1787.  
  1788.         elsif key = ESCAPE then
  1789.             -- special command
  1790.             get_escape(FALSE)
  1791.  
  1792.         elsif key = CR then
  1793.             if searching then
  1794.             searching = search(TRUE)
  1795.             normal_video()
  1796.             searching = TRUE -- avoids accidental <CR> insertion
  1797.             else
  1798.             try_auto_complete(key)
  1799.             end if
  1800.         
  1801.         else
  1802.             insert(key)
  1803.  
  1804.         end if
  1805.  
  1806.         adding_to_kill = FALSE
  1807.  
  1808.         end if
  1809.  
  1810.         if key != CR and key != ESCAPE then
  1811.         searching = FALSE
  1812.         end if
  1813.         cursor(ED_CURSOR)
  1814.         set_position(s_line, s_col)
  1815.     
  1816.     else
  1817.         -- illegal key pressed
  1818.         get_escape(TRUE)  -- give him some help
  1819.         set_position(s_line, s_col)
  1820.     end if
  1821.     end while
  1822. end procedure
  1823.  
  1824. procedure ed(sequence command)
  1825. -- editor main procedure 
  1826. -- start editing a new file
  1827. -- ed.ex is executed by ed.bat
  1828. -- command line will be:
  1829. --    ex ed.ex              - get filename from ex.err, or user
  1830. --    ex ed.ex filename     - filename specified
  1831.  
  1832.     file_number file_no
  1833.  
  1834.     start_line = 0
  1835.     start_col = 0
  1836.  
  1837.     if length(command) >= 3 then
  1838.     file_name = lower(command[3])
  1839.     else
  1840.     file_name = get_err_line()
  1841.     end if
  1842.     if length(file_name) = 0 then
  1843.     -- we still don't know the file name - so ask user
  1844.     puts(SCREEN, "file name: ")
  1845.     cursor(ED_CURSOR)
  1846.     file_name = key_gets("")
  1847.     puts(SCREEN, '\n')
  1848.     end if
  1849.     file_name = delete_trailing_white(file_name)
  1850.     if length(file_name) = 0 then
  1851.     abort(1) -- file_name was just whitespace - quit
  1852.     end if
  1853.     file_no = open(file_name, "r")
  1854.  
  1855.     -- turn off multi_color & auto_complete for non .e files
  1856.     multi_color = WANT_COLOR_SYNTAX
  1857.     auto_complete = WANT_AUTO_COMPLETE
  1858.     if not config[VC_COLOR] or config[VC_MODE] = 7 then
  1859.     multi_color = FALSE -- mono monitor
  1860.     end if
  1861.     file_name = file_name & ' '
  1862.     dot_e = FALSE
  1863.     for i = 1 to length(E_FILES) do
  1864.     if match(E_FILES[i] & ' ', file_name) then
  1865.         dot_e = TRUE
  1866.     end if
  1867.     end for
  1868.     if not dot_e then
  1869.     multi_color = FALSE
  1870.     auto_complete = FALSE
  1871.     end if
  1872.     
  1873.     -- use PROG_INDENT tab width for Euphoria & other languages:
  1874.     edit_tab_width = STANDARD_TAB_WIDTH
  1875.     for i = 1 to length(PROG_FILES) do
  1876.        if match(PROG_FILES[i] & ' ', file_name) then
  1877.        edit_tab_width = PROG_INDENT
  1878.        exit
  1879.        end if
  1880.     end for
  1881.     
  1882.     file_name = file_name[1..length(file_name)-1] -- remove ' '
  1883.     if multi_color then
  1884.     init_class()
  1885.     end if
  1886.  
  1887.     adding_to_kill = FALSE
  1888.     clear_modified()
  1889.     buffer_version = 0
  1890.     control_chars = FALSE
  1891.     wrap(0)
  1892.     new_buffer()
  1893.     s_line = 1
  1894.     s_col = 1
  1895.     b_line = 1
  1896.     b_col = 1
  1897.     save_state()
  1898.     if file_no = -1 then
  1899.     set_f_line(window_number, " <new file>")
  1900.     normal_video()
  1901.     ClearWindow()
  1902.     else
  1903.     set_f_line(window_number, "")
  1904.     normal_video()
  1905.     set_position(1, 1)
  1906.     cursor(NO_CURSOR)
  1907.     read_file(file_no)
  1908.     close(file_no)
  1909.     end if
  1910.     set_position(1, 1)
  1911.     edit_file()
  1912. end procedure
  1913.  
  1914. procedure ed_main()
  1915. -- startup and shutdown of ed()
  1916.     sequence cl
  1917.     
  1918.     config = video_config()
  1919.  
  1920.     if config[VC_XPIXELS] > 0 then
  1921.     if graphics_mode(3) then
  1922.     end if
  1923.     config = video_config()
  1924.     end if
  1925.  
  1926.     if config[VC_LINES] != INITIAL_LINES then
  1927.     screen_length = text_rows(INITIAL_LINES)
  1928.     config = video_config()
  1929.     end if
  1930.     screen_length = config[VC_LINES]
  1931.     window_length = screen_length - 1
  1932.  
  1933.     cl = command_line()
  1934.  
  1935.     while length(window_list) > 0 do
  1936.     ed(cl)
  1937.     cl = {"ex", "ed.ex" , file_name}
  1938.     end while
  1939.  
  1940.     -- exit editor
  1941.     if screen_length != FINAL_LINES then
  1942.     screen_length = text_rows(FINAL_LINES)
  1943.     end if
  1944.     cursor(UNDERLINE_CURSOR)
  1945.     bk_color(BLACK)
  1946.     text_color(BLACK)
  1947.     position(screen_length, 1)
  1948.     puts(SCREEN, BLANK_LINE)
  1949.     position(screen_length, 1)
  1950.     text_color(WHITE)
  1951.     puts(SCREEN, " \n")
  1952. end procedure
  1953.  
  1954. ed_main()
  1955. -- This abort statement reduces the chance of 
  1956. -- a syntax error when you edit ed.ex using itself: 
  1957. abort(0) 
  1958.  
  1959.